package com.geek.gkpush.biz.config;

import com.geek.gkpush.framework.dispatch.SpringCtxUtils;
import com.geek.gkpush.util.json.JacksonUtils;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.event.EventListener;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.stereotype.Component;
import org.springframework.util.ReflectionUtils;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * <h1>线程池配置变更事件监听器。</h1><br/>
 *
 * <pre>
 *     至于是怎么找到这个监听事件的，可以通过源码的更新，稍微看一下日志，然后根据日志的逆向 debug 追踪，就会找到这个监听事件机制。
 *
 *     日志打印的变更内容：
 *     2023-10-01 23:35:23.297  WARN 37222 --- [ternal.notifier] c.a.c.n.c.NacosPropertySourceBuilder     : Ignore the empty nacos configuration and get it based on dataId[nacos-dynamic-common.properties] & group[DEFAULT_GROUP]
 *     2023-10-01 23:35:23.305  WARN 37222 --- [ternal.notifier] c.a.c.n.c.NacosPropertySourceBuilder     : Ignore the empty nacos configuration and get it based on dataId[nacos-dynamic-global.properties] & group[DEFAULT_GROUP]
 *     2023-10-01 23:35:23.321  WARN 37222 --- [ternal.notifier] c.a.c.n.c.NacosPropertySourceBuilder     : Ignore the empty nacos configuration and get it based on dataId[nacos-dynamic-env] & group[DEFAULT_GROUP]
 *     2023-10-01 23:35:23.331  WARN 37222 --- [ternal.notifier] c.a.c.n.c.NacosPropertySourceBuilder     : Ignore the empty nacos configuration and get it based on dataId[nacos-dynamic-env.properties] & group[DEFAULT_GROUP]
 *
 * </pre>
 *
 * @author hmilyylimh
 *         ^_^
 * @version 0.0.1
 *         ^_^
 * @date 2023/10/4
 *
 */
@Slf4j
@Component
public class ExecutorConfigChangeListener {

    public static final String SPECAIL_PREFIX = "Executor_Config_Ctrl_";

    @EventListener(value = EnvironmentChangeEvent.class)
    public void executorChanged(EnvironmentChangeEvent event) {
        ConfigurableApplicationContext ctx = (ConfigurableApplicationContext) event.getSource();
        ConfigurableEnvironment environment = ctx.getEnvironment();

        Set<String> keys = event.getKeys();
        for (String key : keys) {
            try {
                onUpdateInner(key, environment.getProperty(key));
            } catch (Exception e) {
                log.error("onUpdateInner error.", e);
            }
        }
    }

    /**
     * <h2>配置变更核心处理逻辑。</h2>
     * <pre>
     *     key = Executor_Config_Ctrl_defaultEventMsgExecutor
     *     value = {"beanName": "defaultEventMsgExecutor","corePoolSize": 2,"maxPoolSize": 5,"maximumPoolSize": 1024}
     * </pre>
     * @param: key
     * @param: value
     * @return: void
     */
    private static void onUpdateInner(String key, String value) throws Exception {
        // 不是需要监听处理的键，那就直接返回不处理
        if (key != null && !key.startsWith(SPECAIL_PREFIX)) {
            log.info(key + " 不是需要监听处理的键.");
            return;
        }

        // 解析 value 数据，解析不出来也直接返回
        ExecutorCfgInfo info = JacksonUtils.fromJson(value, ExecutorCfgInfo.class);
        if (info == null) {
            return;
        }

        // 修改核心线程数
        ThreadPoolExecutor poolExecutor = (ThreadPoolExecutor) SpringCtxUtils.getBean(info.getBeanName());
        if (info.getCorePoolSize() > 0) {
            poolExecutor.setCorePoolSize(info.getCorePoolSize());
        }

        // 修改最大线程数
        if (info.getMaxPoolSize() > 0) {
            poolExecutor.setMaximumPoolSize(info.getMaxPoolSize());
        }

        // 修改队列容量上限值
        if (info.getMaximumPoolSize() > 0) {
            BlockingQueue<Runnable> queue = poolExecutor.getQueue();
            if (queue instanceof LinkedBlockingQueue) {
                Field capacityField = ReflectionUtils.findField(LinkedBlockingQueue.class, "capacity");
                ReflectionUtils.makeAccessible(capacityField);
                capacityField.set(queue, info.getMaximumPoolSize());
            }
        }
    }

    @Setter
    @Getter
    @ToString
    static class ExecutorCfgInfo implements Serializable {

        private static final long serialVersionUID = -7740442350881602577L;

        /** <h2>线程池在 Spring 上下文中的 bean 名称。</h2> **/
        private String beanName;

        /** <h2>核心线程数</h2> **/
        private int corePoolSize;

        /** <h2>最大线程数</h2> **/
        private int maxPoolSize;

        /** <h2>队列大小</h2> **/
        private int maximumPoolSize;
    }
}